##
## This file is part of the django-oidauth project.
##
## Copyright (C) 2009 Filippo Santovito <http://mementomori.myvidoop.com/>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License version 3 as
## published by the Free Software Foundation;
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
##

from django.shortcuts import render_to_response
from django.http import HttpResponse, HttpResponseRedirect
from django.utils.encoding import smart_unicode
from django.core.urlresolvers import reverse
from django.contrib.auth import login, authenticate
from django.contrib.auth.models import User
from django.template import RequestContext


from openid.consumer.consumer import Consumer, SUCCESS #, CANCEL, FAILURE, SETUP_NEEDED
from openid.consumer.discover import DiscoveryFailure
from openid.extensions.sreg import SRegRequest, SRegResponse
from openid.store.filestore import FileOpenIDStore

from oidauth.forms import OpenidSigninForm
from oidauth.utils import get_trust_root, get_store_path, create_user_profile


def remote_auth(request, openid_url):
    """
    doc at: http://openidenabled.com/files/python-openid/docs/2.2.1/openid.consumer.consumer-module.html

    here we do steps 2 and 3 from the 'Overview' paragraph

    the consumer store is a simple FileOpenIDStore:
    doc at: http://openidenabled.com/files/python-openid/docs/2.2.1/openid.store.filestore.FileOpenIDStore-class.html
    """

    path = get_store_path()
    consumer = Consumer(request.session, FileOpenIDStore(path))

    try:
        auth_request = consumer.begin(openid_url)
    except DiscoveryFailure:
        return HttpResponse('invalid openid')

    #TODO: find a way to setup required/optional data dinamically
    sreg_request = SRegRequest(required=['email'],optional=['nickname',
                                                            'fullname',
                                                            'dob',
                                                            'gender',
                                                            'postcode',
                                                            'country',
                                                            'language',
                                                            'timezone'])
    trust_root = get_trust_root()
    redirect_to = trust_root+ reverse('oidauth_response')

    if sreg_request:
        auth_request.addExtension(sreg_request)

    redirect_url = auth_request.redirectURL(trust_root, redirect_to)

    return HttpResponseRedirect(redirect_url)



def local_auth(request, username, password):
    """
    try to login via username/password
    """
    user = authenticate(username=username, password=password)
    if user is not None:
        if user.is_active:
            login(request, user)
        else:
            return HttpResponse("user %s inactive" % (username,))
    else:
        return HttpResponse("user %s doesn't exist" % (username,))

    return HttpResponseRedirect(reverse('oidauth_profile'))


def signin(request):
    """
    If called in a GET request show an empty form
    else if the openid_url contains the OpenID url
    try to use login via the OpenID server.
    If openid_url is blank use username/password to login.
    """

    if request.POST:
        form = OpenidSigninForm(request, data=request.POST)

        if form.is_valid():
            openid_url = form.cleaned_data['openid_url']
            username = form.cleaned_data['username']
            password = form.cleaned_data['password']
            if openid_url:
                return remote_auth(request, openid_url)
            else:
                return local_auth(request, username, password)
    else:
        form = OpenidSigninForm()
        request.session.set_test_cookie()
        
    return render_to_response('oidauth/signin.html',
                              {'form':form},
                              context_instance=RequestContext(request))

def profile(request):
    """
    redirect to the user profile page
    """
    return render_to_response('oidauth/profile.html',
                              context_instance=RequestContext(request))


def provider_response(request):
    """
    doc at: http://openidenabled.com/files/python-openid/docs/2.2.1/openid.consumer.consumer-module.html

    here we do step 4 from the 'Overview' paragraph

    """
    path = get_store_path()
    consumer = Consumer(request.session, FileOpenIDStore(path))

    #params in utf8
    params = dict((k,smart_unicode(v)) for k, v in request.GET.items())

    url = get_trust_root() + reverse('oidauth_response')
    response = consumer.complete(params, url)
            
    
    if response.status == SUCCESS:
        identity_url =  response.identity_url            
        sreg_response = SRegResponse.fromSuccessResponse(response)

        #create user if he doesn't exist
        user, created = User.objects.get_or_create(username=identity_url)

        if created:
            create_user_profile(user, identity_url, sreg_response)

        user = authenticate(openid_url=identity_url)
        if user is not None:
            if user.is_active:
                login(request, user)
                return HttpResponseRedirect(reverse('oidauth_profile'))
            else:
                return HttpResponse("user %s inactive" % (identity_url,))
                
        else:
            """
            happens when:

            a local user (without an openid profile) already exists with username == identity_url
            so we can't accept the second arrived.
            SOLUTION: don't let local user creating username like valid openid urls (TODO)

            it will happen when you will reset the oidauth app without removing the paired local
            users.
            """

            return HttpResponse("%s identity already taken" % (identity_url,))

    return HttpResponse(response.status)
    
    #elif response.status == CANCEL:
    #    return HttpResponse('The request was canceled')
    #elif response.status == FAILURE:
    #    return HttpResponse('failure')
    #elif response.status == SETUP_NEEDED:
    #    return HttpResponse('Setup needed')
    #return HttpResponse("Bad openid status: %s" % response.status)
        

